package com.example.apprank.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.apprank.common.domain.PageQueryDTO;
import com.example.apprank.common.domain.TableDataInfo;
import com.example.apprank.executor.PythonExecutor;
import com.example.apprank.mapper.AppAndroidMapper;
import com.example.apprank.mapper.AppIosMapper;
import com.example.apprank.module.AppAndroid;
import com.example.apprank.module.AppIos;
import com.example.apprank.service.impl.IAppService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


@Service
public class AppServiceImpl implements IAppService {
    private final String IOS_TYPE = "ios";
    private final String ANDROID_TYPE = "android";

    @Autowired
    private AppIosMapper appIosMapper;

    @Autowired
    private AppAndroidMapper appAndroidMapper;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private PythonExecutor executor;

    @Override
    public  TableDataInfo getApp(PageQueryDTO pageQueryDTO) {
        String osType = pageQueryDTO.getOs();

        // 使用 final 常量避免魔法字符串
        if (ANDROID_TYPE.equalsIgnoreCase(osType)) {
            // 如果 os 是 "ios"，则调用通用搜索方法并传入 AppIos.class
            Pair<List<AppAndroid>, Long> pair = performSearch(pageQueryDTO, AppAndroid.class);
            return  TableDataInfo.success(pair.getKey(), pair.getValue());
        } else {
            // 否则，调用通用搜索方法并传入 AppAndroid.class
            Pair<List<AppIos>, Long> pair = performSearch(pageQueryDTO, AppIos.class);
            return  TableDataInfo.success(pair.getKey(), pair.getValue());
        }
    }

    /**
     * 通用的、私有的搜索执行方法
     *
     * @param pageQueryDTO 查询条件 DTO
     * @param clazz        目标实体类的 Class 对象 (例如 AppIos.class 或 AppAndroid.class)
     * @param <T>          泛型类型，代表实体类
     * @return 返回一个包含查询结果和分页信息的 Page 对象
     */
    private <T> Pair<List<T>,Long> performSearch(PageQueryDTO pageQueryDTO, Class<T> clazz) {
        String keyWord = pageQueryDTO.getKeyWord();
        String osType = pageQueryDTO.getOs();
        String category = pageQueryDTO.getCategory();
        Pageable pageable = PageRequest.of(
                pageQueryDTO.getPageNum() - 1,
                pageQueryDTO.getPageSize(),
                Sort.by(Sort.Direction.DESC, "priceValue")
        );

        NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder();
        nativeQueryBuilder.withPageable(pageable);

        nativeQueryBuilder.withQuery(q -> q
                .bool(b -> {
                    if (!StrUtil.isEmpty(keyWord)) {
                        b.must(m -> m
                                .multiMatch(mm -> mm
                                        .query(keyWord)
                                        .fields("name", "publisherName")
                                )
                        );
                    } else {
                        b.must(m -> m.matchAll(ma -> ma));
                    }
                    //这里的话，在前端的时候系统是默认的
                    //当点击种类的时候，将种类参数给传过来，然后将中类参数进行过滤
                    //这里还涉及的问题就是当数据库中数据更新的时候，同时要更新ES中的数据
                    if (!StrUtil.isEmpty(osType)) {
                        b.filter(f -> f
                                .term(t -> t
                                        .field("os.keyword")
                                        .value(osType)
                                )
                        );
                    }
                    if(!StrUtil.isEmpty(category)) {
                        b.filter(f->f
                                .term(t->t
                                        .field("category.keyword")
                                        .value(category)
                                )
                        );
                    }
                    return b;
                })
        );

        NativeQuery searchQuery = nativeQueryBuilder.build();

        SearchHits<T> searchHits = elasticsearchTemplate.search(searchQuery, clazz);
        Long count = elasticsearchTemplate.count(searchQuery, clazz);
        List<T> appList = searchHits.getSearchHits().stream()
                .map(SearchHit::getContent)
                .collect(Collectors.toList());
        return new Pair<>(appList,count);
    }

    @Override
    public AppIos selectIosAppById(Long appId) {
        if (appId == null) {
            return null;
        }

        AppIos appIos = elasticsearchTemplate.get(appId.toString(), AppIos.class);

        return appIos;
    }
    @Override
    public AppAndroid selectAndroidAppById(String appId) {
        if (StrUtil.isEmpty(appId)) {
            return null;
        }

        AppAndroid appAndroid = elasticsearchTemplate.get(appId, AppAndroid.class);
        String appDetailUrl = appAndroid.getAppOverviewUrl();
        return appAndroid;
    }

    @Override
    public Integer getAppNum() {
        List<AppIos> appIosList = appIosMapper.selectList(new LambdaQueryWrapper<>());
        List<AppAndroid> appAndroidList = appAndroidMapper.selectList(new LambdaQueryWrapper<>());
        if (CollectionUtil.isEmpty(appIosList) && CollectionUtil.isEmpty(appAndroidList)) {
            return 0;
        } else if (CollectionUtil.isEmpty(appIosList) || CollectionUtil.isEmpty(appAndroidList)) {
            return CollectionUtil.isEmpty(appIosList) ? appAndroidList.size() : appIosList.size();
        } else{
            return appIosList.size() + appAndroidList.size();
        }
    }

    @Override
    public List<? extends Object> getAppByES(String keyWord) {
        List<?> appsList = new ArrayList<>();
        return List.of();
    }

    @Override
    public void savaDataES() {
        List<AppIos> appIosList = appIosMapper.selectList(new LambdaQueryWrapper<AppIos>());
        elasticsearchTemplate.save(appIosList);
        System.out.println(appIosList.size());
        List<AppAndroid> appAndroidList = appAndroidMapper.selectList(new LambdaQueryWrapper<AppAndroid>());
        elasticsearchTemplate.save(appAndroidList);
        System.out.println(appAndroidList.size());

    }

    @Override
    public Integer getCategory() {
        return appIosMapper.getCategoryNum();
    }

    @Override
    public Long getRevenueSum() {
        Long revenueIos = appIosMapper.getRevenueSumIos();
        Long revenueAndroid = appAndroidMapper.getRevenueSumAndroid();
        return revenueIos + revenueAndroid;
    }

    @Override
    public List<String> getPngUrls(String appDetailUrl ) throws Exception {
        List<String> pngUrlsList = executor.executeScript(appDetailUrl);
        if(CollectionUtil.isEmpty(pngUrlsList)) {
            return null;
        }
        return pngUrlsList;
    }
}
